#!/usr/bin/env python3
"""
AIQuant requirements-dev.txt 诊断脚本
分析开发依赖文件中的潜在问题
"""

import re
import sys
import json
from pathlib import Path
from collections import defaultdict, Counter

class RequirementsDiagnostic:
    def __init__(self, dev_requirements_path: str, prod_requirements_path: str):
        self.dev_requirements_path = Path(dev_requirements_path)
        self.prod_requirements_path = Path(prod_requirements_path)
        self.dev_packages = {}
        self.prod_packages = {}
        self.issues = {}
        
    def parse_requirements_file(self, file_path: Path):
        """解析requirements文件"""
        packages = {}
        
        with open(file_path, 'r', encoding='utf-8') as f:
            for line_num, line in enumerate(f, 1):
                line = line.strip()
                
                # 跳过注释和空行
                if not line or line.startswith('#') or line.startswith('-r'):
                    continue
                
                # 处理平台特定依赖
                if ';' in line:
                    package_part, condition = line.split(';', 1)
                    line = package_part.strip()
                
                # 解析包名和版本
                match = re.match(r'^([a-zA-Z0-9_-]+(?:\[[^\]]+\])?)\s*([><=!~]+.*)?$', line)
                if match:
                    package_name = match.group(1).lower()
                    version_spec = match.group(2) or ""
                    packages[package_name] = {
                        'version_spec': version_spec,
                        'line_num': line_num,
                        'original_line': line
                    }
        
        return packages
    
    def check_duplicate_dependencies(self):
        """检查重复依赖"""
        print("🔍 检查重复依赖...")
        
        # 检查dev文件内部重复
        package_counts = Counter()
        for line_num, line in enumerate(open(self.dev_requirements_path, 'r'), 1):
            line = line.strip()
            if line and not line.startswith('#') and not line.startswith('-r'):
                if ';' in line:
                    line = line.split(';')[0].strip()
                match = re.match(r'^([a-zA-Z0-9_-]+)', line)
                if match:
                    package_name = match.group(1).lower()
                    package_counts[package_name] += 1
        
        duplicates = {pkg: count for pkg, count in package_counts.items() if count > 1}
        if duplicates:
            self.issues['duplicates'] = list(duplicates.items())
            print(f"❌ 发现 {len(duplicates)} 个重复依赖:")
            for pkg, count in duplicates.items():
                print(f"   - {pkg}: 出现 {count} 次")
        else:
            print("✅ 未发现重复依赖")
    
    def check_version_conflicts(self):
        """检查版本冲突"""
        print("\n🔍 检查版本冲突...")
        
        conflicts = []
        
        # 检查dev和prod之间的版本冲突
        for pkg_name in self.dev_packages:
            if pkg_name in self.prod_packages:
                dev_info = self.dev_packages[pkg_name]
                prod_info = self.prod_packages[pkg_name]
                dev_spec = dev_info.get('version_spec', '')
                prod_spec = prod_info.get('version_spec', '')
                
                if dev_spec and prod_spec and dev_spec != prod_spec:
                    conflicts.append({
                        'package': pkg_name,
                        'dev_version': dev_spec,
                        'prod_version': prod_spec
                    })
        
        if conflicts:
            self.issues['version_conflicts'] = conflicts
            print(f"❌ 发现 {len(conflicts)} 个版本冲突:")
            for conflict in conflicts:
                print(f"   - {conflict['package']}: dev({conflict['dev_version']}) vs prod({conflict['prod_version']})")
        else:
            print("✅ 未发现版本冲突")
    
    def check_outdated_packages(self):
        """检查过时的包"""
        print("\n🔍 检查过时包...")
        
        # 已知的过时包列表
        deprecated_packages = {
            'pdb++': '建议使用 ipdb 或 pudb',
            'pathlib2': 'Python 3.4+ 内置 pathlib',
            'testing.postgresql': '建议使用 pytest-postgresql',
            'testing.redis': '建议使用 pytest-redis',
            'docker-compose': '已被 docker compose 替代',
        }
        
        outdated = []
        for pkg_name in self.dev_packages:
            if pkg_name in deprecated_packages:
                outdated.append({
                    'package': pkg_name,
                    'reason': deprecated_packages[pkg_name]
                })
        
        if outdated:
            self.issues['outdated_packages'] = outdated
            print(f"⚠️ 发现 {len(outdated)} 个过时包:")
            for pkg in outdated:
                print(f"   - {pkg['package']}: {pkg['reason']}")
        else:
            print("✅ 未发现明显过时的包")
    
    def check_security_issues(self):
        """检查安全问题"""
        print("\n🔍 检查安全问题...")
        
        # 已知有安全问题的版本
        security_issues = {
            'pillow': {'<10.0.0': 'CVE-2023-44271'},
            'cryptography': {'<41.0.0': 'Multiple CVEs'},
            'requests': {'<2.31.0': 'CVE-2023-32681'},
        }
        
        security_warnings = []
        for pkg_name, pkg_info in self.dev_packages.items():
            if pkg_name in security_issues:
                version_spec = pkg_info.get('version_spec', '')
                for vulnerable_version, cve in security_issues[pkg_name].items():
                    security_warnings.append({
                        'package': pkg_name,
                        'current_version': version_spec,
                        'vulnerable_version': vulnerable_version,
                        'cve': cve
                    })
        
        if security_warnings:
            self.issues['security_issues'] = security_warnings
            print(f"🚨 发现 {len(security_warnings)} 个潜在安全问题:")
            for warning in security_warnings:
                print(f"   - {warning['package']}: {warning['cve']}")
        else:
            print("✅ 未发现明显安全问题")
    
    def check_best_practices(self):
        """检查最佳实践"""
        print("\n🔍 检查最佳实践...")
        
        best_practice_issues = []
        
        # 检查版本固定
        unpinned_packages = []
        for pkg_name, pkg_info in self.dev_packages.items():
            version_spec = pkg_info.get('version_spec', '')
            if not version_spec or not any(op in version_spec for op in ['==', '~=']):
                unpinned_packages.append(pkg_name)
        
        if unpinned_packages:
            best_practice_issues.append({
                'type': 'unpinned_versions',
                'packages': unpinned_packages[:10],  # 只显示前10个
                'count': len(unpinned_packages)
            })
        
        # 检查包名规范
        irregular_names = []
        for pkg_name in self.dev_packages:
            if '_' in pkg_name and '-' in pkg_name:
                irregular_names.append(pkg_name)
        
        if irregular_names:
            best_practice_issues.append({
                'type': 'irregular_names',
                'packages': irregular_names
            })
        
        if best_practice_issues:
            self.issues['best_practices'] = best_practice_issues
            print(f"⚠️ 发现 {len(best_practice_issues)} 类最佳实践问题:")
            for issue in best_practice_issues:
                if issue['type'] == 'unpinned_versions':
                    print(f"   - 未固定版本的包: {issue['count']} 个")
                elif issue['type'] == 'irregular_names':
                    print(f"   - 包名不规范: {', '.join(issue['packages'])}")
        else:
            print("✅ 符合基本最佳实践")
    
    def check_platform_dependencies(self):
        """检查平台特定依赖"""
        print("\n🔍 检查平台特定依赖...")
        
        platform_deps = []
        with open(self.dev_requirements_path, 'r') as f:
            for line_num, line in enumerate(f, 1):
                if ';' in line and 'sys_platform' in line:
                    platform_deps.append({
                        'line': line.strip(),
                        'line_num': line_num
                    })
        
        if platform_deps:
            print(f"ℹ️ 发现 {len(platform_deps)} 个平台特定依赖:")
            for dep in platform_deps:
                print(f"   - 第{dep['line_num']}行: {dep['line']}")
        else:
            print("ℹ️ 未发现平台特定依赖")
    
    def generate_report(self):
        """生成诊断报告"""
        print("\n" + "="*80)
        print("📊 AIQuant requirements-dev.txt 诊断报告")
        print("="*80)
        
        total_issues = sum(len(issues) if isinstance(issues, list) else 1 for issues in self.issues.values())
        
        if total_issues == 0:
            print("🎉 恭喜！未发现严重问题")
        else:
            print(f"⚠️ 总计发现 {total_issues} 个问题需要关注")
        
        # 按严重程度分类
        critical_issues = ['security_issues', 'version_conflicts']
        warning_issues = ['duplicates', 'outdated_packages']
        info_issues = ['best_practices']
        
        severity_counts = {
            'critical': sum(len(self.issues.get(issue, [])) if isinstance(self.issues.get(issue, []), list) else 1 for issue in critical_issues if issue in self.issues),
            'warning': sum(len(self.issues.get(issue, [])) if isinstance(self.issues.get(issue, []), list) else 1 for issue in warning_issues if issue in self.issues),
            'info': sum(len(self.issues.get(issue, [])) if isinstance(self.issues.get(issue, []), list) else 1 for issue in info_issues if issue in self.issues)
        }
        
        print(f"\n严重程度分布:")
        print(f"🚨 严重: {severity_counts['critical']} 个")
        print(f"⚠️ 警告: {severity_counts['warning']} 个")
        print(f"ℹ️ 信息: {severity_counts['info']} 个")
        
        # 保存详细报告到文件
        report_file = Path('requirements_dev_diagnostic_report.json')
        with open(report_file, 'w', encoding='utf-8') as f:
            json.dump({
                'summary': {
                    'total_packages': len(self.dev_packages),
                    'total_issues': total_issues,
                    'severity_counts': severity_counts
                },
                'issues': self.issues
            }, f, indent=2, ensure_ascii=False)
        
        print(f"\n📄 详细报告已保存到: {report_file}")
        
        return self.issues
    
    def run_diagnosis(self):
        """运行完整诊断"""
        print("🚀 开始 AIQuant requirements-dev.txt 诊断...")
        print(f"📁 开发依赖文件: {self.dev_requirements_path}")
        print(f"📁 生产依赖文件: {self.prod_requirements_path}")
        
        # 解析文件
        self.dev_packages = self.parse_requirements_file(self.dev_requirements_path)
        self.prod_packages = self.parse_requirements_file(self.prod_requirements_path)
        
        print(f"📦 开发依赖包数量: {len(self.dev_packages)}")
        print(f"📦 生产依赖包数量: {len(self.prod_packages)}")
        
        # 执行各项检查
        self.check_duplicate_dependencies()
        self.check_version_conflicts()
        self.check_outdated_packages()
        self.check_security_issues()
        self.check_best_practices()
        self.check_platform_dependencies()
        
        # 生成报告
        return self.generate_report()

def main():
    """主函数"""
    dev_file = "requirements-dev.txt"
    prod_file = "requirements.txt"
    
    if not Path(dev_file).exists():
        print(f"❌ 文件不存在: {dev_file}")
        sys.exit(1)
    
    if not Path(prod_file).exists():
        print(f"❌ 文件不存在: {prod_file}")
        sys.exit(1)
    
    diagnostic = RequirementsDiagnostic(dev_file, prod_file)
    issues = diagnostic.run_diagnosis()
    
    return len(issues) > 0

if __name__ == "__main__":
    sys.exit(main())